#!/usr/bin/python3
# -*- coding: utf-8 -*-
import datetime
import decimal
import json
import os
import re
import time
import urllib.parse

import flask
import pymysql
import pymysql.cursors


class Db(object):
    def __init__(self):
        self.conn = None
        self.cursor = None
        self.connect()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()

    def connect(self):
        self.conn = pymysql.connect(
            host=DB_CONFIG_GALLERY["host"],
            port=DB_CONFIG_GALLERY["port"],
            user=DB_CONFIG_GALLERY["user"],
            password=DB_CONFIG_GALLERY["passwd"],
            database=DB_CONFIG_GALLERY["db"],
            charset=DB_CONFIG_GALLERY["charset"]
        )
        self.cursor = self.conn.cursor(cursor=pymysql.cursors.DictCursor)

    def close(self):
        self.cursor.close()
        self.conn.close()

    def call(self, client, site):
        start = time.time()
        sql = "SELECT %s AS `client`, COUNT(*) AS `count` FROM `webstats`.`call`" \
              " WHERE `timestamp` >= CURRENT_DATE AND `site` = %s"
        res, status, msg = [], 1, ""
        try:
            self.cursor.execute(sql, (client, site))
            res = self.cursor.fetchall()
        except Exception as e:
            status, msg = 0, str(e)
        return res, status, msg, int((time.time() - start) * 1000)

    def log(self, site, path, query, body, url, user, fingerprint, ip,
            user_agent, browser, os_name, screen, region):
        sql = "INSERT INTO `webstats`.`call`" \
              "(`site`, `path`, `query`, `body`, `url`, `user`, `fingerprint`, `ip`," \
              " `useragent`, `browser`, `os`, `screen`, `region`)" \
              " VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"
        try:
            self.cursor.execute(sql, (
                site, path, query, body, url, user, fingerprint, ip,
                user_agent, browser, os_name, screen, region
            ))
            self.conn.commit()
        except Exception as e:
            self.conn.rollback()
            print(str(e))


class JsonEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            return obj.strftime('%Y-%m-%d %H:%M:%S')
        elif isinstance(obj, datetime.date):
            return obj.strftime('%Y-%m-%d')
        elif isinstance(obj, decimal.Decimal):
            _, _, exponent = obj.as_tuple()
            if exponent >= 0:  # No digits after the decimal point.
                return int(obj)
            return float(obj)
        else:
            return json.JSONEncoder.default(self, obj)


class JsonProvider(flask.json.provider.JSONProvider):
    def dumps(self, obj, **kwargs):
        return json.dumps(obj, **kwargs, cls=JsonEncoder)

    def loads(self, s, **kwargs):
        return json.loads(s, **kwargs)


def log():
    if DEBUG:
        return
    path = flask.request.path
    query = flask.request.query_string
    data = flask.request.data
    url = flask.request.url
    # user = auth.current_user()  # 仅当注解过 @auth.login_required 的接口有效
    user = flask.request.authorization.username if flask.request.authorization else None  # 所有接口有效
    fingerprint = urllib.parse.unquote(flask.request.cookies.get("fingerprint"))\
        if flask.request.cookies.get("fingerprint") else None
    ip = flask.request.headers.get("X-Forwarded-For", flask.request.remote_addr)
    user_agent = flask.request.user_agent.string
    browser = urllib.parse.unquote(flask.request.cookies.get("browser"))\
        if flask.request.cookies.get("browser") else None
    os_name = urllib.parse.unquote(flask.request.cookies.get("os"))\
        if flask.request.cookies.get("os") else None
    screen = urllib.parse.unquote(flask.request.cookies.get("screen"))\
        if flask.request.cookies.get("screen") else None
    region = urllib.parse.unquote(flask.request.cookies.get("region"))\
        if flask.request.cookies.get("region") else None
    with Db() as db:
        db.log(ID, path, query, data, url, user, fingerprint, ip,
               user_agent, browser, os_name, screen, region)


# def get_auth_code():
#     # data_user = auth.get_auth()  # 仅当注解过 @auth.login_required 的接口有效
#     data_user = flask.request.authorization  # 所有接口有效
#     code = "%s:%s" % (data_user.username if data_user else "", data_user.password if data_user else "")
#     code = str(base64.b64encode(code.encode("utf-8")), encoding="utf-8")
#     return "Basic %s" % code


def parse_date(text):
    """
    除去除首尾空格，不处理原文本，避免误判自定规则的 @xxx、!xxx、pxxx
    可用格式例:
    2023年5月30日, 2023年05月30, 2023-05-30, 2023/5/30, 2023.5.30
    23年5月30日, 23年05月30, 23-05-30, 23/5/30, 23.5.30
    5月30日, 05月30, 05-30, 5/30, 5.30
    20230530, 230530, 0530
    """
    if not text:
        return None
    text = text.strip()
    try:
        m = re.match("(\\d{4})[年\\-/.](\\d{1,2})[月\\-/.](\\d{1,2})日?", text)
        if m:
            return datetime.datetime.strptime("%s-%s-%s" % (
                m.group(1), m.group(2), m.group(3)
            ), "%Y-%m-%d")
        m = re.match("(\\d{2})[年\\-/.](\\d{1,2})[月\\-/.](\\d{1,2})日?", text)
        if m:
            return datetime.datetime.strptime("%s-%s-%s" % (
                m.group(1), m.group(2), m.group(3)
            ), "%y-%m-%d")
        m = re.match("(\\d{1,2})[月\\-/.](\\d{1,2})日?", text)
        if m:
            return datetime.datetime.strptime("%s-%s-%s" % (
                datetime.date.today().year, m.group(1), m.group(2)
            ), "%Y-%m-%d")
        # text = str(max(re.findall("\\d+", text), key=len))  # 找出最长连续数字串
        # text = re.sub("\\D+", "", text)  # 剔除非数字（合并所有数字）
        if len(text) == 8:
            return datetime.datetime.strptime(text, "%Y%m%d")
        if len(text) == 6:
            return datetime.datetime.strptime(text, "%y%m%d")
        if len(text) == 4:
            return datetime.datetime.strptime("%d%s" % (datetime.date.today().year, text), "%Y%m%d")
    except ValueError:
        pass
    return None


# static_url_path 解决 nginx 反向代理导致找不到 static 资源
# server = flask.Flask(__name__, static_url_path="/3g/static")
server = flask.Flask(__name__)

# server.json_provider_class = JsonProvider  # 统一日期格式
server.json = JsonProvider(server)  # 统一日期格式

# https://www.bookstack.cn/read/head-first-flask/chapter03-section3.04.md
# auth_admin = flask_httpauth.HTTPBasicAuth()


# @auth_admin.verify_password
# def verify_admin(user, pwd):
#     with Db() as db:
#         data = db.auth_account(user, pwd)
#     # flask.g.auth = data
#     return data[1] == 1 and data[0]["admin"] == 1


@server.route("/favicon.ico", methods=["GET"])
def favicon():
    # 兼容
    return server.send_static_file("img/favicon.ico")


@server.route("/", methods=["GET"])
def homepage():
    return flask.redirect("/random", code=302)
    # or return flask.redirect(flask.url_for("home"), code=302)


@server.route("/stats", methods=["GET", "POST"])
def stats():
    with Db() as db:
        data_call = db.call(CLIENT, ID)
    return flask.jsonify({
        "status": 1,
        "msg": data_call[2],
        "stats": {
            "call": data_call[0] if data_call[1] == 1 else -1,
            "cost": "call %d" % data_call[3]
        },
        "version": "%s.%d" % (VERSION, PORT),
        "timestamp": datetime.datetime.now()
    })


@server.route("/home", methods=["GET"])
def home():
    log()
    return flask.render_template(
        "gallery.html",
        client=CLIENT,
        provider_id=ID,
        provider_name=NAME,
        provider_name_short=NAME_SHORT,
        page="/home",
        api="%s/snake/cover?client=%s" % (API_HOST, CLIENT),
        split="",
        raw_provider="{rawprovider}",
        raw_id="{rawid}",
        cover="cover",
        blink=0,
        cate_how="",
        cate_what="",
        delete=0,
        dislike=0,
        r18=0,
        save=0,
        tag="{catename}",
        total=999,
        group=0,
        group_total=24,
        shuffle=0
    )


@server.route("/date", methods=["GET"])
@server.route("/reldate", methods=["GET"])
@server.route("/latest", methods=["GET"])
def latest():
    log()
    return flask.render_template(
        "gallery.html",
        client=CLIENT,
        provider_id=ID,
        provider_name=NAME,
        provider_name_short=NAME_SHORT,
        page="/latest",
        api="%s/v2?client=%s&order=date&no={no}&id={id}" % (API_ROOT, CLIENT),
        split="no",
        raw_provider=ID,
        raw_id="{id}",
        cover="cover",
        blink=1,
        cate_how="",
        cate_what="",
        delete=0,
        dislike=0,
        r18=0,
        save=0,
        tag="",
        total=300,
        group=1,
        group_total=24,
        shuffle=0
    )


@server.route("/score", methods=["GET"])
@server.route("/trending", methods=["GET"])
def trending():
    log()
    return flask.render_template(
        "gallery.html",
        client=CLIENT,
        provider_id=ID,
        provider_name=NAME,
        provider_name_short=NAME_SHORT,
        page="/trending",
        api="%s/v2?client=%s&order=score&score={score}&id={id}" % (API_ROOT, CLIENT),
        split="score",
        raw_provider=ID,
        raw_id="{id}",
        cover="cover",
        blink=1,
        cate_how="",
        cate_what="",
        delete=0,
        dislike=0,
        r18=0,
        save=0,
        tag="",
        total=300,
        group=0,
        group_total=24,
        shuffle=0
    )


@server.route("/random", methods=["GET"])
def random():
    log()
    return flask.render_template(
        "gallery.html",
        client=CLIENT,
        provider_id=ID,
        provider_name=NAME,
        provider_name_short=NAME_SHORT,
        page="/random",
        api="%s/v2?client=%s&order=random&seed={seed}&id={id}" % (API_ROOT, CLIENT),
        split="seed",
        raw_provider=ID,
        raw_id="{id}",
        cover="cover",
        blink=1,
        cate_how="",
        cate_what="",
        delete=0,
        dislike=0,
        r18=0,
        save=0,
        tag="",
        total=300,
        group=0,
        group_total=24,
        shuffle=0
    )


@server.route("/seek", methods=["GET"])
def seek():
    log()
    return flask.render_template(
        "gallery.html",
        client=CLIENT,
        provider_id=ID,
        provider_name=NAME,
        provider_name_short=NAME_SHORT,
        page="/seek",
        api="%s/recommend?client=%s&user={user}&pwd={pwd}&device={device}&id={id}" % (API_ROOT, CLIENT),
        split="",
        raw_provider=ID,
        raw_id="{id}",
        cover="cover",
        blink=1,
        cate_how="",
        cate_what="",
        delete=0,
        dislike=0,
        r18=0,
        save=0,
        tag="",
        total=300,
        group=0,
        group_total=24,
        shuffle=0
    )


@server.route("/favorite", methods=["GET"])
def favorite():
    log()
    return flask.render_template(
        "gallery.html",
        client=CLIENT,
        provider_id=ID,
        provider_name=NAME,
        provider_name_short=NAME_SHORT,
        page="/favorite",
        api="%s/favorite?client=%s&user={user}&pwd={pwd}&device={device}&order=date&id={id}" % (API_ROOT, CLIENT),
        split="",
        raw_provider=ID,
        raw_id="{id}",
        cover="cover",
        blink=1,
        cate_how="",
        cate_what="",
        delete=0,
        dislike=1,
        r18=0,
        save=0,
        tag="",
        total=9999,
        group=0,
        group_total=24,
        shuffle=0
    )


@server.route("/mark", methods=["GET"])
# @auth_admin.login_required
def mark():
    log()
    return flask.render_template(
        "gallery.html",
        client=CLIENT,
        provider_id=ID,
        provider_name=NAME,
        provider_name_short=NAME_SHORT,
        page="/mark",
        api="%s/v2?client=%s&user={user}&pwd={pwd}&order=score&score={score}&catehow=general&catewhat=general" % (
            API_ROOT, CLIENT
        ),
        split="score",
        raw_provider=ID,
        raw_id="{id}",
        cover="cover",
        blink=1,
        cate_how="%s/cate/how?client=%s" % (API_ROOT, CLIENT),
        cate_what="%s/cate/what?client=%s" % (API_ROOT, CLIENT),
        delete=1,
        dislike=0,
        r18=1,
        save=0,
        tag="",
        total=9999,
        group=0,
        group_total=24,
        shuffle=0
    )


@server.route("/unaudited", methods=["GET"])
@server.route("/todo", methods=["GET"])
# @auth_admin.login_required
def unaudited():
    log()
    return flask.render_template(
        "gallery.html",
        client=CLIENT,
        provider_id=ID,
        provider_name=NAME,
        provider_name_short=NAME_SHORT,
        page="/unaudited",
        api="%s/unaudited?client=%s&user={user}&pwd={pwd}" % (API_ROOT, CLIENT),
        split="",
        raw_provider=ID,
        raw_id="{id}",
        cover="contain",
        blink=0,
        cate_how="%s/cate/how?client=%s" % (API_ROOT, CLIENT),
        cate_what="%s/cate/what?client=%s" % (API_ROOT, CLIENT),
        delete=1,
        dislike=0,
        r18=1,
        save=0,
        tag="",
        total=9999,
        group=0,
        group_total=24,
        shuffle=0
    )


@server.route("/flip", methods=["GET"])
def flip():
    return flask.redirect("/flip/random", code=302)


@server.route("/flip/date", methods=["GET"])
@server.route("/flip/reldate", methods=["GET"])
@server.route("/flip/latest", methods=["GET"])
def flip_latest():
    log()
    return flask.render_template(
        "flip.html",
        client=CLIENT,
        provider_id=ID,
        provider_name=NAME,
        provider_name_short=NAME_SHORT,
        page="/flip/latest",
        api="%s/v2?client=%s&order=date&no={no}" % (API_ROOT, CLIENT),
        split="no",
        title="{title}",
        title_alt="",
        copyright="@{copyright}",
        cover="cover",
        raw_provider=ID,
        raw_id="{id}",
        fix_api="",
        fix_hint=""
    )


@server.route("/flip/score", methods=["GET"])
@server.route("/flip/trending", methods=["GET"])
def flip_trending():
    log()
    return flask.render_template(
        "flip.html",
        client=CLIENT,
        provider_id=ID,
        provider_name=NAME,
        provider_name_short=NAME_SHORT,
        page="/flip/trending",
        api="%s/v2?client=%s&order=score&score={score}" % (API_ROOT, CLIENT),
        split="score",
        title="{title}",
        title_alt="",
        copyright="@{copyright}",
        cover="cover",
        raw_provider=ID,
        raw_id="{id}",
        fix_api="",
        fix_hint=""
    )


@server.route("/flip/random", methods=["GET"])
def flip_random():
    log()
    return flask.render_template(
        "flip.html",
        client=CLIENT,
        provider_id=ID,
        provider_name=NAME,
        provider_name_short=NAME_SHORT,
        page="/flip/random",
        api="%s/v2?client=%s&order=random&seed={seed}" % (API_ROOT, CLIENT),
        split="seed",
        title="{title}",
        title_alt="",
        copyright="@{copyright}",
        cover="cover",
        raw_provider=ID,
        raw_id="{id}",
        fix_api="",
        fix_hint=""
    )


@server.route("/flip/seek", methods=["GET"])
def flip_seek():
    log()
    return flask.render_template(
        "flip.html",
        client=CLIENT,
        provider_id=ID,
        provider_name=NAME,
        provider_name_short=NAME_SHORT,
        page="/flip/seek",
        api="%s/recommend?client=%s&user={user}&pwd={pwd}&device={device}&id={id}" % (API_ROOT, CLIENT),
        split="",
        title="{title}",
        title_alt="",
        copyright="@{copyright}",
        cover="cover",
        raw_provider=ID,
        raw_id="{id}",
        fix_api="",
        fix_hint=""
    )


@server.route("/flip/favorite", methods=["GET"])
def flip_favorite():
    log()
    return flask.render_template(
        "flip.html",
        client=CLIENT,
        provider_id=ID,
        provider_name=NAME,
        provider_name_short=NAME_SHORT,
        page="/flip/favorite",
        api="%s/favorite?client=%s&user={user}&pwd={pwd}&device={device}&order=date" % (API_ROOT, CLIENT),
        split="",
        title="{title}",
        title_alt="",
        copyright="@{copyright}",
        cover="cover",
        raw_provider=ID,
        raw_id="{id}",
        fix_api="",
        fix_hint=""
    )


@server.route("/flip/mark", methods=["GET"])
# @auth_admin.login_required
def flip_mark():
    log()
    return flask.render_template(
        "flip.html",
        client=CLIENT,
        provider_id=ID,
        provider_name=NAME,
        provider_name_short=NAME_SHORT,
        page="/flip/mark",
        api="%s/v2?client=%s&user={user}&pwd={pwd}&order=score&score={score}&catehow=general&catewhat=general" % (
            API_ROOT, CLIENT
        ),
        split="score",
        title="{title}",
        title_alt="",
        copyright="@{copyright}",
        cover="contain",
        raw_provider=ID,
        raw_id="{id}",
        fix_api="",
        fix_hint=""
    )


@server.route("/flip/unaudited", methods=["GET"])
@server.route("/flip/todo", methods=["GET"])
# @auth_admin.login_required
def flip_unaudited():
    log()
    return flask.render_template(
        "flip.html",
        client=CLIENT,
        provider_id=ID,
        provider_name=NAME,
        provider_name_short=NAME_SHORT,
        page="/flip/unaudited",
        api="%s/unaudited?client=%s&user={user}&pwd={pwd}" % (API_ROOT, CLIENT),
        split="",
        title="{title}",
        title_alt="",
        copyright="@{copyright}",
        cover="contain",
        raw_provider=ID,
        raw_id="{id}",
        fix_api="",
        fix_hint=""
    )


@server.route("/flip/<string:tag>", methods=["GET"])
def flip_target(tag):
    """
    支持可解析的日期值、以 ! 开头的图片 ID、以 @ © 开头的版权方或作者，其他则视为关键字
    """
    log()
    date = parse_date(tag)
    if date:
        return flask.render_template(
            "flip.html",
            client=CLIENT,
            provider_id=ID,
            provider_name=NAME,
            provider_name_short=NAME_SHORT,
            page="/flip/target",
            api="%s/v2?client=%s&order=date&date=%s&no={no}" % (API_ROOT, CLIENT, date.strftime("%Y%m%d")),
            split="no",
            title="{title}",
            title_alt="",
            copyright="@{copyright}",
            cover="cover",
            raw_provider=ID,
            raw_id="{id}",
            fix_api="",
            fix_hint=""
        )
    elif re.match("![^!]+", tag):
        return flask.render_template(
            "flip.html",
            client=CLIENT,
            provider_id=ID,
            provider_name=NAME,
            provider_name_short=NAME_SHORT,
            page="/flip/target",
            api="%s/v2?client=%s&order=random&seed={seed}&id=%s" % (API_ROOT, CLIENT, tag[1:]),
            split="seed",
            title="{title}",
            title_alt="",
            copyright="@{copyright}",
            cover="cover",
            raw_provider=ID,
            raw_id="{id}",
            fix_api="",
            fix_hint=""
        )
    return flask.render_template(
        "flip.html",
        client=CLIENT,
        provider_id=ID,
        provider_name=NAME,
        provider_name_short=NAME_SHORT,
        page="/flip/target",
        api="%s/v2?client=%s&order=date&tag=%s" % (API_ROOT, CLIENT, tag),
        split="",
        title="{title}",
        title_alt="",
        copyright="@{copyright}",
        cover="cover",
        raw_provider=ID,
        raw_id="{id}",
        fix_api="",
        fix_hint=""
    )


@server.route("/account", methods=["GET"])
def account():
    log()
    return flask.render_template(
        "account.html",
        client=CLIENT,
        provider_id=ID,
        cover_img="%s/random?json=1" % API_ROOT
    )


@server.route("/about", methods=["GET"])
def about():
    log()
    return flask.render_template(
        "about.html",
        client=CLIENT,
        provider_id=ID,
        provider_name=NAME,
        provider_name_short=NAME_SHORT,
        slogan=SLOGAN,
        doc=API_ROOT + "/doc",
        api=API_HOST + "/appstats/version"
    )


@server.route("/<string:tag>", methods=["GET"])
def target(tag):
    """
    支持可解析的日期值、以 ! 开头的图片 ID、以 @ © 开头的版权方或作者，其他则视为关键字
    """
    log()
    date = parse_date(tag)
    if date:
        return flask.render_template(
            "gallery.html",
            client=CLIENT,
            provider_id=ID,
            provider_name=NAME,
            provider_name_short=NAME_SHORT,
            page="/target",
            api="%s/v2?client=%s&order=date&date=%s&no={no}" % (API_ROOT, CLIENT, date.strftime("%Y%m%d")),
            split="no",
            raw_provider=ID,
            raw_id="{id}",
            cover="cover",
            blink=1,
            cate_how="",
            cate_what="",
            delete=0,
            dislike=0,
            r18=0,
            save=0,
            tag="{reldate}",
            total=300,
            group=1,
            group_total=24,
            shuffle=0
        )
    elif re.match("![^!]+", tag):
        return flask.render_template(
            "gallery.html",
            client=CLIENT,
            provider_id=ID,
            provider_name=NAME,
            provider_name_short=NAME_SHORT,
            page="/target",
            api="%s/v2?client=%s&order=random&seed={seed}&id=%s" % (API_ROOT, CLIENT, tag[1:]),
            split="seed",
            raw_provider=ID,
            raw_id="{id}",
            cover="cover",
            blink=1,
            cate_how="",
            cate_what="",
            delete=0,
            dislike=0,
            r18=0,
            save=0,
            tag="",
            total=300,
            group=0,
            group_total=24,
            shuffle=0
        )
    return flask.render_template(
        "gallery.html",
        client=CLIENT,
        provider_id=ID,
        provider_name=NAME,
        provider_name_short=NAME_SHORT,
        page="/target",
        api="%s/v2?client=%s&order=date&tag=%s" % (API_ROOT, CLIENT, tag),
        split="",
        raw_provider=ID,
        raw_id="{id}",
        cover="cover",
        blink=1,
        cate_how="",
        cate_what="",
        delete=0,
        dislike=0,
        r18=0,
        save=0,
        tag="",
        total=9999,
        group=0,
        group_total=24,
        shuffle=0
    )


if __name__ == "__main__":
    ID = "3g"
    NAME, NAME_SHORT = "3G壁纸", "3G"
    SLOGAN = "电脑壁纸专家"
    VERSION = "1.1.230319"
    CLIENT = "timelineweb"
    API_HOST = "https://api.nguaduot.cn"
    API_ROOT = "%s/%s" % (API_HOST, ID)
    if re.search("(\\d{4})\\.py$", __file__):
        PORT = int(re.search("(\\d{4})\\.py$", __file__).group(1))
        DEBUG = False
    else:
        PORT = 80
        DEBUG = True
    FILE_CONFIG = os.path.join(os.path.dirname(os.path.dirname(__file__)), "data/config.json")
    with open(FILE_CONFIG, "r", encoding="utf-8") as f:
        DB_CONFIG = json.loads(f.read())
    if DEBUG:
        DB_CONFIG_GALLERY = DB_CONFIG[ID]["debug"]
        server.run(debug=True, port=PORT, host="0.0.0.0")
    else:
        DB_CONFIG_GALLERY = DB_CONFIG[ID]["release"]
        server.run(debug=False, port=PORT, host="localhost")
